This notebook defines the most focal recurrent copy number units by removing focal changes that are within entire chromosome arm losses and gains. Most focal here meaning:

  • If a chromosome arm is not clearly defined as a gain or loss (and is callable) we look to define the cytoband level status
  • If a cytoband is not clearly defined as a gain or loss (and is callable) we then look to define the gene-level status

Usage

This notebook is intended to be run from the command line with the following (assumes you are in the root directory of the repository):

Rscript -e "rmarkdown::render('analyses/focal-cn-file-preparation/05-define-most-focal-cn-units.Rmd', clean = TRUE)"

Cutoffs:

# The fraction of calls a particular status needs to be
# above to be called the majority status -- the decision
# for a cutoff of 90% here was made to ensure that the status
# is not only the majority status but it is also significantly
# called more than the other status values in the region
status_threshold <- 0.9

# The fraction threshold for determining if enough of a region
# (arm, cytoband, or gene) is callable to determine its status --
# the decision for a cutoff of 50% here was made as it seems reasonable
# to expect a region to be more than 50% callable for a dominant status
# call to be made
uncallable_threshold <- 0.5

Set up

Libraries and functions

library(tidyverse)
── Attaching packages ───────────────────────────────────────────────────────── tidyverse 1.2.1 ──
✔ ggplot2 3.2.0     ✔ purrr   0.3.2
✔ tibble  2.1.3     ✔ dplyr   0.8.3
✔ tidyr   0.8.3     ✔ stringr 1.4.0
✔ readr   1.3.1     ✔ forcats 0.4.0
── Conflicts ──────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()

Files and directories

results_dir <- "results"

Read in files

Read in cytoband status file and format it for what we will need in this notebook.

# Read in the file with consensus CN status data and the UCSC cytoband data --
# generated in `03-add-cytoband-status-consensus.Rmd`
consensus_seg_cytoband_status_df <-
  read_tsv(file.path("results", "consensus_seg_with_ucsc_cytoband_status.tsv.gz")) %>%
  # Need this to not have `chr`
  mutate(
    chr = gsub("chr", "", chr),
    cytoband = paste0(chr, cytoband)
  ) %>%
  select(
    chromosome_arm,
    # Distinguish this dominant status that is based on cytobands, from the status
    dominant_cytoband_status = dominant_status,
    cytoband,
    Kids_First_Biospecimen_ID,
    band_length,
    gain_fraction,
    loss_fraction,
    callable_fraction
  )
Parsed with column specification:
cols(
  Kids_First_Biospecimen_ID = col_character(),
  chr = col_character(),
  cytoband = col_character(),
  dominant_status = col_character(),
  band_length = col_double(),
  callable_fraction = col_double(),
  gain_fraction = col_double(),
  loss_fraction = col_double(),
  chromosome_arm = col_character()
)

Read in the gene-level data.

# Read in the annotated gene level CN file
consensus_seg_autosomes_df <-
  read_tsv(file.path(results_dir, "consensus_seg_annotated_cn_autosomes.tsv.gz")) %>%
  mutate(chromosome_arm = gsub("(p|q).*", "\\1", cytoband))
Parsed with column specification:
cols(
  biospecimen_id = col_character(),
  status = col_character(),
  copy_number = col_double(),
  ploidy = col_double(),
  ensembl = col_character(),
  gene_symbol = col_character(),
  cytoband = col_character()
)
# Rename "amplification" status calls to be "gain" for the purpose of this script
consensus_seg_autosomes_df$status <-
  gsub("amplification", "gain", consensus_seg_autosomes_df$status)

Define most focal units

Determine chromosome arm status

consensus_seg_arm_status <- consensus_seg_cytoband_status_df %>%
    # Group by biospecimen ID and region
    group_by(Kids_First_Biospecimen_ID, chromosome_arm) %>%
    # Summarize the weighted means for each status
    summarize(
      loss_fraction_arm = weighted.mean(loss_fraction, band_length),
      gain_fraction_arm = weighted.mean(gain_fraction, band_length),
      callable_fraction_arm = weighted.mean(callable_fraction, band_length)
    ) %>%
    # Define dominant status based the weighted means meeting a status
    # threshold
    mutate(
      dominant_arm_status = case_when(
        callable_fraction_arm < (1 - uncallable_threshold) ~ "uncallable",
        loss_fraction_arm > status_threshold ~ "loss",
        gain_fraction_arm > status_threshold ~ "gain",
        loss_fraction_arm + gain_fraction_arm > status_threshold ~ "unstable",
        TRUE ~ "neutral"
      )
    )

# Display table
consensus_seg_arm_status

Determine cytoband status

We want to include cytoband and gene-level calls for chromosome arms that have not been defined as a gain or loss to make the cytoband-level majority calls.

# Now define the cytoband as that status if more than the `status_threshold` 
# fraction value of the total counts are for that particular status
consensus_seg_cytoband_status <-
  consensus_seg_cytoband_status_df %>%
  mutate(
    dominant_cytoband_status = case_when(
      callable_fraction < (1 - uncallable_threshold) ~ "uncallable",
      loss_fraction > status_threshold ~ "loss",
      gain_fraction > status_threshold ~ "gain",
      loss_fraction + gain_fraction > status_threshold ~ "unstable",
      TRUE ~ "neutral"
    )
  ) 

# Join the consensus seg arm status data and filter to include only neutral
# chromosome arms and non-neutral cytobands
filtered_consensus_cytoband_status <- consensus_seg_cytoband_status %>%
  left_join(
    consensus_seg_arm_status,
    by = c(
      "Kids_First_Biospecimen_ID",
      "chromosome_arm"
    )
  ) %>%
  # Filter the annotated CN data to include only neutral chromosome arms and disagreements
  filter(
    dominant_arm_status %in% c("neutral", "uncallable", "unstable") |
      (
        dominant_cytoband_status != dominant_arm_status &
          # bands that disagree with arm, but are not neutral (or uncallable)
          !(dominant_cytoband_status %in% c("neutral", "uncallable", "unstable"))
      )
  ) %>%
  select(
    Kids_First_Biospecimen_ID,
    cytoband,
    loss_fraction,
    gain_fraction,
    callable_fraction,
    dominant_cytoband_status
  )

# Display table
filtered_consensus_cytoband_status

Determine gene-level status

# Create separate rows for genes span multiple cytobands
# CAUTION: This will require addition of a distinct() statment later to resolve duplicates
# Filtering to only multiband genes first because regexes are slow.
multi_band_genes <- consensus_seg_autosomes_df %>%
  filter(grepl("-", cytoband)) %>%
  extract(cytoband, into = c("chrom", "band"), regex = "([0-9]+)(.+)") %>% # make chrom and band cols
  separate_rows(band, sep = "-") %>% # duplicate rows with more than one band
  unite(cytoband, chrom, band, sep = "") # rejoin chrom and band

# Filter to singleband genes
single_band_genes <- consensus_seg_autosomes_df %>%
  filter(!grepl("-", cytoband))
gene_df <- bind_rows(single_band_genes, multi_band_genes)

# Now create a data.frame with the gene-level status calls for the
# neutral, uncallable, and unstable cytoband-level and chromosome arm-level
# calls
filtered_consensus_gene_status <- gene_df %>%
  left_join(
    consensus_seg_arm_status,
    by = c("biospecimen_id" = "Kids_First_Biospecimen_ID",
           "chromosome_arm")
  ) %>%
  left_join(
    consensus_seg_cytoband_status,
    by = c("biospecimen_id" = "Kids_First_Biospecimen_ID",
           "cytoband")
  ) %>%
  # Filter the annotated CN data to include only neutral arms and cytobands,
  # and disagreements
  filter(
    # Case 1) Gene call disagrees with both arm and cytoband (This captures most
    # we want to keep, including all of when arm and cytoband are neutral, uncallable
    # or unstable, since we have no neutral gene calls in this df):
    (
      status != dominant_arm_status & status != dominant_cytoband_status
    )
    # Case 2) Gene call disagrees with a non-neutral cytoband call.
    # Keep no matter what the arm status
    | (
      status != dominant_cytoband_status
      & dominant_cytoband_status %in% c("gain", "loss")
    )
    # I think that captures everything we want. Cases we don't want include:
    # gene & arm agree, but cytoband is neutral
    # gene & cytoband agree
    # all 3 agree
  ) %>%
  select(Kids_First_Biospecimen_ID = biospecimen_id,
         gene_symbol,
         status) %>%
  # The `distinct()` function is needed to remove duplicates resulting from
  # the band separation into multiple rows step above
  distinct()

# Display table
filtered_consensus_gene_status

Combine arm, cytoband, and gene-level status data

# Rename each the dominant status columns of the data.frames
# to be uniform for the binding rows step and filter out "neutral" calls
consensus_seg_arm_status <- consensus_seg_arm_status %>%
  filter(!(dominant_arm_status == "neutral")) %>%
  rename(dominant_status = dominant_arm_status)

filtered_consensus_cytoband_status <- filtered_consensus_cytoband_status %>%
  filter(!(dominant_cytoband_status == "neutral")) %>%
  rename(dominant_status = dominant_cytoband_status)

# There are no "neutral" calls at the gene level so we do not need to filter
# out those calls here
filtered_consensus_gene_status <- filtered_consensus_gene_status %>%
  rename(dominant_status = status)

# For each of the datasets we're joining, we'll only keep these columns:
cols_to_keep <- c("Kids_First_Biospecimen_ID", "dominant_status")

Combine into one long data frame

# Combine the arm, cytoband, and gene status count data
final_df <- bind_rows(
  arm = select(consensus_seg_arm_status,
               cols_to_keep,
               region = chromosome_arm),
  cytoband = select(filtered_consensus_cytoband_status,
                    cols_to_keep,
                    region = cytoband),
  gene = select(filtered_consensus_gene_status,
                cols_to_keep,
                region = gene_symbol),
  .id = "region_type"
) %>%
  # Reorder columns more sensibly
  select(Kids_First_Biospecimen_ID,
         status = dominant_status,
         region,
         region_type) %>%
  arrange(Kids_First_Biospecimen_ID)

# Print out preview
final_df

Write to a TSV file

# Write final long status table to file
write_tsv(
  final_df,
  file.path(results_dir, "consensus_seg_most_focal_cn_status.tsv.gz")
)

# Display final long status table
final_df %>%
  arrange(region_type)

Session Info

sessionInfo()
R version 3.6.0 (2019-04-26)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Debian GNU/Linux 9 (stretch)

Matrix products: default
BLAS/LAPACK: /usr/lib/libopenblasp-r0.2.19.so

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
 [3] LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
 [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=C             
 [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                 
 [9] LC_ADDRESS=C               LC_TELEPHONE=C            
[11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] forcats_0.4.0   stringr_1.4.0   dplyr_0.8.3     purrr_0.3.2    
[5] readr_1.3.1     tidyr_0.8.3     tibble_2.1.3    ggplot2_3.2.0  
[9] tidyverse_1.2.1

loaded via a namespace (and not attached):
 [1] Rcpp_1.0.1       cellranger_1.1.0 pillar_1.4.2     compiler_3.6.0  
 [5] base64enc_0.1-3  tools_3.6.0      digest_0.6.20    lubridate_1.7.4 
 [9] jsonlite_1.6     evaluate_0.14    nlme_3.1-140     gtable_0.3.0    
[13] lattice_0.20-38  pkgconfig_2.0.2  rlang_0.4.0      cli_1.1.0       
[17] rstudioapi_0.10  yaml_2.2.0       haven_2.1.1      xfun_0.8        
[21] withr_2.1.2      xml2_1.2.0       httr_1.4.0       knitr_1.23      
[25] hms_0.4.2        generics_0.0.2   grid_3.6.0       tidyselect_0.2.5
[29] glue_1.3.1       R6_2.4.0         readxl_1.3.1     rmarkdown_1.13  
[33] modelr_0.1.4     magrittr_1.5     backports_1.1.4  scales_1.0.0    
[37] htmltools_0.3.6  rvest_0.3.4      assertthat_0.2.1 colorspace_1.4-1
[41] stringi_1.4.3    lazyeval_0.2.2   munsell_0.5.0    broom_0.5.2     
[45] crayon_1.3.4    
LS0tCnRpdGxlOiAiRmluZCBtb3N0IGZvY2FsIHJlY3VycmVudCBjb3B5IG51bWJlciB1bml0cyIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOgogICAgdG9jOiBUUlVFCiAgICB0b2NfZmxvYXQ6IFRSVUUKYXV0aG9yOiBDaGFudGUgQmV0aGVsbCBhbmQgQ2FuZGFjZSBTYXZvbmVuIGZvciBBTFNGIENDREwKZGF0ZTogMjAyMAotLS0KClRoaXMgbm90ZWJvb2sgZGVmaW5lcyB0aGUgbW9zdCBmb2NhbCByZWN1cnJlbnQgY29weSBudW1iZXIgdW5pdHMgYnkgcmVtb3ZpbmcgZm9jYWwgY2hhbmdlcyB0aGF0IGFyZSB3aXRoaW4gZW50aXJlIGNocm9tb3NvbWUgYXJtIGxvc3NlcyBhbmQgZ2FpbnMuCl9Nb3N0IGZvY2FsXyBoZXJlIG1lYW5pbmc6CgotIElmIGEgY2hyb21vc29tZSBhcm0gaXMgbm90IGNsZWFybHkgZGVmaW5lZCBhcyBhIGdhaW4gb3IgbG9zcyAoYW5kIGlzIGNhbGxhYmxlKSB3ZSBsb29rIHRvIGRlZmluZSB0aGUgY3l0b2JhbmQgbGV2ZWwgc3RhdHVzCi0gSWYgYSBjeXRvYmFuZCBpcyBub3QgY2xlYXJseSBkZWZpbmVkIGFzIGEgZ2FpbiBvciBsb3NzIChhbmQgaXMgY2FsbGFibGUpIHdlIHRoZW4gbG9vayB0byBkZWZpbmUgdGhlIGdlbmUtbGV2ZWwgc3RhdHVzCgojIyBVc2FnZQoKVGhpcyBub3RlYm9vayBpcyBpbnRlbmRlZCB0byBiZSBydW4gZnJvbSB0aGUgY29tbWFuZCBsaW5lIHdpdGggdGhlIGZvbGxvd2luZyAoYXNzdW1lcyB5b3UgYXJlIGluIHRoZSByb290IGRpcmVjdG9yeSBvZiB0aGUgcmVwb3NpdG9yeSk6CgpgYGAKUnNjcmlwdCAtZSAicm1hcmtkb3duOjpyZW5kZXIoJ2FuYWx5c2VzL2ZvY2FsLWNuLWZpbGUtcHJlcGFyYXRpb24vMDUtZGVmaW5lLW1vc3QtZm9jYWwtY24tdW5pdHMuUm1kJywgY2xlYW4gPSBUUlVFKSIKYGBgCgojIyMgQ3V0b2ZmczogCgpgYGB7cn0KIyBUaGUgZnJhY3Rpb24gb2YgY2FsbHMgYSBwYXJ0aWN1bGFyIHN0YXR1cyBuZWVkcyB0byBiZQojIGFib3ZlIHRvIGJlIGNhbGxlZCB0aGUgbWFqb3JpdHkgc3RhdHVzIC0tIHRoZSBkZWNpc2lvbgojIGZvciBhIGN1dG9mZiBvZiA5MCUgaGVyZSB3YXMgbWFkZSB0byBlbnN1cmUgdGhhdCB0aGUgc3RhdHVzCiMgaXMgbm90IG9ubHkgdGhlIG1ham9yaXR5IHN0YXR1cyBidXQgaXQgaXMgYWxzbyBzaWduaWZpY2FudGx5CiMgY2FsbGVkIG1vcmUgdGhhbiB0aGUgb3RoZXIgc3RhdHVzIHZhbHVlcyBpbiB0aGUgcmVnaW9uCnN0YXR1c190aHJlc2hvbGQgPC0gMC45CgojIFRoZSBmcmFjdGlvbiB0aHJlc2hvbGQgZm9yIGRldGVybWluaW5nIGlmIGVub3VnaCBvZiBhIHJlZ2lvbgojIChhcm0sIGN5dG9iYW5kLCBvciBnZW5lKSBpcyBjYWxsYWJsZSB0byBkZXRlcm1pbmUgaXRzIHN0YXR1cyAtLQojIHRoZSBkZWNpc2lvbiBmb3IgYSBjdXRvZmYgb2YgNTAlIGhlcmUgd2FzIG1hZGUgYXMgaXQgc2VlbXMgcmVhc29uYWJsZQojIHRvIGV4cGVjdCBhIHJlZ2lvbiB0byBiZSBtb3JlIHRoYW4gNTAlIGNhbGxhYmxlIGZvciBhIGRvbWluYW50IHN0YXR1cwojIGNhbGwgdG8gYmUgbWFkZQp1bmNhbGxhYmxlX3RocmVzaG9sZCA8LSAwLjUKYGBgCgojIyBTZXQgdXAKCiMjIyBMaWJyYXJpZXMgYW5kIGZ1bmN0aW9ucwoKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQpgYGAKCiMjIyBGaWxlcyBhbmQgZGlyZWN0b3JpZXMKCmBgYHtyfQpyZXN1bHRzX2RpciA8LSAicmVzdWx0cyIKYGBgCgojIyMgUmVhZCBpbiBmaWxlcwoKUmVhZCBpbiBjeXRvYmFuZCBzdGF0dXMgZmlsZSBhbmQgZm9ybWF0IGl0IGZvciB3aGF0IHdlIHdpbGwgbmVlZCBpbiB0aGlzIG5vdGVib29rLiAKCmBgYHtyfQojIFJlYWQgaW4gdGhlIGZpbGUgd2l0aCBjb25zZW5zdXMgQ04gc3RhdHVzIGRhdGEgYW5kIHRoZSBVQ1NDIGN5dG9iYW5kIGRhdGEgLS0KIyBnZW5lcmF0ZWQgaW4gYDAzLWFkZC1jeXRvYmFuZC1zdGF0dXMtY29uc2Vuc3VzLlJtZGAKY29uc2Vuc3VzX3NlZ19jeXRvYmFuZF9zdGF0dXNfZGYgPC0KICByZWFkX3RzdihmaWxlLnBhdGgoInJlc3VsdHMiLCAiY29uc2Vuc3VzX3NlZ193aXRoX3Vjc2NfY3l0b2JhbmRfc3RhdHVzLnRzdi5neiIpKSAlPiUKICAjIE5lZWQgdGhpcyB0byBub3QgaGF2ZSBgY2hyYAogIG11dGF0ZSgKICAgIGNociA9IGdzdWIoImNociIsICIiLCBjaHIpLAogICAgY3l0b2JhbmQgPSBwYXN0ZTAoY2hyLCBjeXRvYmFuZCkKICApICU+JQogIHNlbGVjdCgKICAgIGNocm9tb3NvbWVfYXJtLAogICAgIyBEaXN0aW5ndWlzaCB0aGlzIGRvbWluYW50IHN0YXR1cyB0aGF0IGlzIGJhc2VkIG9uIGN5dG9iYW5kcywgZnJvbSB0aGUgc3RhdHVzCiAgICBkb21pbmFudF9jeXRvYmFuZF9zdGF0dXMgPSBkb21pbmFudF9zdGF0dXMsCiAgICBjeXRvYmFuZCwKICAgIEtpZHNfRmlyc3RfQmlvc3BlY2ltZW5fSUQsCiAgICBiYW5kX2xlbmd0aCwKICAgIGdhaW5fZnJhY3Rpb24sCiAgICBsb3NzX2ZyYWN0aW9uLAogICAgY2FsbGFibGVfZnJhY3Rpb24KICApCmBgYAoKUmVhZCBpbiB0aGUgZ2VuZS1sZXZlbCBkYXRhLiAKCmBgYHtyfQojIFJlYWQgaW4gdGhlIGFubm90YXRlZCBnZW5lIGxldmVsIENOIGZpbGUKY29uc2Vuc3VzX3NlZ19hdXRvc29tZXNfZGYgPC0KICByZWFkX3RzdihmaWxlLnBhdGgocmVzdWx0c19kaXIsICJjb25zZW5zdXNfc2VnX2Fubm90YXRlZF9jbl9hdXRvc29tZXMudHN2Lmd6IikpICU+JQogIG11dGF0ZShjaHJvbW9zb21lX2FybSA9IGdzdWIoIihwfHEpLioiLCAiXFwxIiwgY3l0b2JhbmQpKQoKIyBSZW5hbWUgImFtcGxpZmljYXRpb24iIHN0YXR1cyBjYWxscyB0byBiZSAiZ2FpbiIgZm9yIHRoZSBwdXJwb3NlIG9mIHRoaXMgc2NyaXB0CmNvbnNlbnN1c19zZWdfYXV0b3NvbWVzX2RmJHN0YXR1cyA8LQogIGdzdWIoImFtcGxpZmljYXRpb24iLCAiZ2FpbiIsIGNvbnNlbnN1c19zZWdfYXV0b3NvbWVzX2RmJHN0YXR1cykKYGBgCgojIyBEZWZpbmUgbW9zdCBmb2NhbCB1bml0cwoKIyMjIERldGVybWluZSBjaHJvbW9zb21lIGFybSBzdGF0dXMKCmBgYHtyfQpjb25zZW5zdXNfc2VnX2FybV9zdGF0dXMgPC0gY29uc2Vuc3VzX3NlZ19jeXRvYmFuZF9zdGF0dXNfZGYgJT4lCiAgICAjIEdyb3VwIGJ5IGJpb3NwZWNpbWVuIElEIGFuZCByZWdpb24KICAgIGdyb3VwX2J5KEtpZHNfRmlyc3RfQmlvc3BlY2ltZW5fSUQsIGNocm9tb3NvbWVfYXJtKSAlPiUKICAgICMgU3VtbWFyaXplIHRoZSB3ZWlnaHRlZCBtZWFucyBmb3IgZWFjaCBzdGF0dXMKICAgIHN1bW1hcml6ZSgKICAgICAgbG9zc19mcmFjdGlvbl9hcm0gPSB3ZWlnaHRlZC5tZWFuKGxvc3NfZnJhY3Rpb24sIGJhbmRfbGVuZ3RoKSwKICAgICAgZ2Fpbl9mcmFjdGlvbl9hcm0gPSB3ZWlnaHRlZC5tZWFuKGdhaW5fZnJhY3Rpb24sIGJhbmRfbGVuZ3RoKSwKICAgICAgY2FsbGFibGVfZnJhY3Rpb25fYXJtID0gd2VpZ2h0ZWQubWVhbihjYWxsYWJsZV9mcmFjdGlvbiwgYmFuZF9sZW5ndGgpCiAgICApICU+JQogICAgIyBEZWZpbmUgZG9taW5hbnQgc3RhdHVzIGJhc2VkIHRoZSB3ZWlnaHRlZCBtZWFucyBtZWV0aW5nIGEgc3RhdHVzCiAgICAjIHRocmVzaG9sZAogICAgbXV0YXRlKAogICAgICBkb21pbmFudF9hcm1fc3RhdHVzID0gY2FzZV93aGVuKAogICAgICAgIGNhbGxhYmxlX2ZyYWN0aW9uX2FybSA8ICgxIC0gdW5jYWxsYWJsZV90aHJlc2hvbGQpIH4gInVuY2FsbGFibGUiLAogICAgICAgIGxvc3NfZnJhY3Rpb25fYXJtID4gc3RhdHVzX3RocmVzaG9sZCB+ICJsb3NzIiwKICAgICAgICBnYWluX2ZyYWN0aW9uX2FybSA+IHN0YXR1c190aHJlc2hvbGQgfiAiZ2FpbiIsCiAgICAgICAgbG9zc19mcmFjdGlvbl9hcm0gKyBnYWluX2ZyYWN0aW9uX2FybSA+IHN0YXR1c190aHJlc2hvbGQgfiAidW5zdGFibGUiLAogICAgICAgIFRSVUUgfiAibmV1dHJhbCIKICAgICAgKQogICAgKQoKIyBEaXNwbGF5IHRhYmxlCmNvbnNlbnN1c19zZWdfYXJtX3N0YXR1cwpgYGAKCiMjIyBEZXRlcm1pbmUgY3l0b2JhbmQgc3RhdHVzCgpXZSB3YW50IHRvIGluY2x1ZGUgY3l0b2JhbmQgYW5kIGdlbmUtbGV2ZWwgY2FsbHMgZm9yIGNocm9tb3NvbWUgYXJtcyB0aGF0IGhhdmUgbm90IGJlZW4gZGVmaW5lZCBhcyBhIGdhaW4gb3IgbG9zcyB0byBtYWtlIHRoZSBjeXRvYmFuZC1sZXZlbCBtYWpvcml0eSBjYWxscy4KCmBgYHtyfQojIE5vdyBkZWZpbmUgdGhlIGN5dG9iYW5kIGFzIHRoYXQgc3RhdHVzIGlmIG1vcmUgdGhhbiB0aGUgYHN0YXR1c190aHJlc2hvbGRgIAojIGZyYWN0aW9uIHZhbHVlIG9mIHRoZSB0b3RhbCBjb3VudHMgYXJlIGZvciB0aGF0IHBhcnRpY3VsYXIgc3RhdHVzCmNvbnNlbnN1c19zZWdfY3l0b2JhbmRfc3RhdHVzIDwtCiAgY29uc2Vuc3VzX3NlZ19jeXRvYmFuZF9zdGF0dXNfZGYgJT4lCiAgbXV0YXRlKAogICAgZG9taW5hbnRfY3l0b2JhbmRfc3RhdHVzID0gY2FzZV93aGVuKAogICAgICBjYWxsYWJsZV9mcmFjdGlvbiA8ICgxIC0gdW5jYWxsYWJsZV90aHJlc2hvbGQpIH4gInVuY2FsbGFibGUiLAogICAgICBsb3NzX2ZyYWN0aW9uID4gc3RhdHVzX3RocmVzaG9sZCB+ICJsb3NzIiwKICAgICAgZ2Fpbl9mcmFjdGlvbiA+IHN0YXR1c190aHJlc2hvbGQgfiAiZ2FpbiIsCiAgICAgIGxvc3NfZnJhY3Rpb24gKyBnYWluX2ZyYWN0aW9uID4gc3RhdHVzX3RocmVzaG9sZCB+ICJ1bnN0YWJsZSIsCiAgICAgIFRSVUUgfiAibmV1dHJhbCIKICAgICkKICApIAoKIyBKb2luIHRoZSBjb25zZW5zdXMgc2VnIGFybSBzdGF0dXMgZGF0YSBhbmQgZmlsdGVyIHRvIGluY2x1ZGUgb25seSBuZXV0cmFsCiMgY2hyb21vc29tZSBhcm1zIGFuZCBub24tbmV1dHJhbCBjeXRvYmFuZHMKZmlsdGVyZWRfY29uc2Vuc3VzX2N5dG9iYW5kX3N0YXR1cyA8LSBjb25zZW5zdXNfc2VnX2N5dG9iYW5kX3N0YXR1cyAlPiUKICBsZWZ0X2pvaW4oCiAgICBjb25zZW5zdXNfc2VnX2FybV9zdGF0dXMsCiAgICBieSA9IGMoCiAgICAgICJLaWRzX0ZpcnN0X0Jpb3NwZWNpbWVuX0lEIiwKICAgICAgImNocm9tb3NvbWVfYXJtIgogICAgKQogICkgJT4lCiAgIyBGaWx0ZXIgdGhlIGFubm90YXRlZCBDTiBkYXRhIHRvIGluY2x1ZGUgb25seSBuZXV0cmFsIGNocm9tb3NvbWUgYXJtcyBhbmQgZGlzYWdyZWVtZW50cwogIGZpbHRlcigKICAgIGRvbWluYW50X2FybV9zdGF0dXMgJWluJSBjKCJuZXV0cmFsIiwgInVuY2FsbGFibGUiLCAidW5zdGFibGUiKSB8CiAgICAgICgKICAgICAgICBkb21pbmFudF9jeXRvYmFuZF9zdGF0dXMgIT0gZG9taW5hbnRfYXJtX3N0YXR1cyAmCiAgICAgICAgICAjIGJhbmRzIHRoYXQgZGlzYWdyZWUgd2l0aCBhcm0sIGJ1dCBhcmUgbm90IG5ldXRyYWwgKG9yIHVuY2FsbGFibGUpCiAgICAgICAgICAhKGRvbWluYW50X2N5dG9iYW5kX3N0YXR1cyAlaW4lIGMoIm5ldXRyYWwiLCAidW5jYWxsYWJsZSIsICJ1bnN0YWJsZSIpKQogICAgICApCiAgKSAlPiUKICBzZWxlY3QoCiAgICBLaWRzX0ZpcnN0X0Jpb3NwZWNpbWVuX0lELAogICAgY3l0b2JhbmQsCiAgICBsb3NzX2ZyYWN0aW9uLAogICAgZ2Fpbl9mcmFjdGlvbiwKICAgIGNhbGxhYmxlX2ZyYWN0aW9uLAogICAgZG9taW5hbnRfY3l0b2JhbmRfc3RhdHVzCiAgKQoKIyBEaXNwbGF5IHRhYmxlCmZpbHRlcmVkX2NvbnNlbnN1c19jeXRvYmFuZF9zdGF0dXMKYGBgCgojIyMgRGV0ZXJtaW5lIGdlbmUtbGV2ZWwgc3RhdHVzCgpgYGB7cn0KIyBDcmVhdGUgc2VwYXJhdGUgcm93cyBmb3IgZ2VuZXMgc3BhbiBtdWx0aXBsZSBjeXRvYmFuZHMKIyBDQVVUSU9OOiBUaGlzIHdpbGwgcmVxdWlyZSBhZGRpdGlvbiBvZiBhIGRpc3RpbmN0KCkgc3RhdG1lbnQgbGF0ZXIgdG8gcmVzb2x2ZSBkdXBsaWNhdGVzCiMgRmlsdGVyaW5nIHRvIG9ubHkgbXVsdGliYW5kIGdlbmVzIGZpcnN0IGJlY2F1c2UgcmVnZXhlcyBhcmUgc2xvdy4KbXVsdGlfYmFuZF9nZW5lcyA8LSBjb25zZW5zdXNfc2VnX2F1dG9zb21lc19kZiAlPiUKICBmaWx0ZXIoZ3JlcGwoIi0iLCBjeXRvYmFuZCkpICU+JQogIGV4dHJhY3QoY3l0b2JhbmQsIGludG8gPSBjKCJjaHJvbSIsICJiYW5kIiksIHJlZ2V4ID0gIihbMC05XSspKC4rKSIpICU+JSAjIG1ha2UgY2hyb20gYW5kIGJhbmQgY29scwogIHNlcGFyYXRlX3Jvd3MoYmFuZCwgc2VwID0gIi0iKSAlPiUgIyBkdXBsaWNhdGUgcm93cyB3aXRoIG1vcmUgdGhhbiBvbmUgYmFuZAogIHVuaXRlKGN5dG9iYW5kLCBjaHJvbSwgYmFuZCwgc2VwID0gIiIpICMgcmVqb2luIGNocm9tIGFuZCBiYW5kCgojIEZpbHRlciB0byBzaW5nbGViYW5kIGdlbmVzCnNpbmdsZV9iYW5kX2dlbmVzIDwtIGNvbnNlbnN1c19zZWdfYXV0b3NvbWVzX2RmICU+JQogIGZpbHRlcighZ3JlcGwoIi0iLCBjeXRvYmFuZCkpCmdlbmVfZGYgPC0gYmluZF9yb3dzKHNpbmdsZV9iYW5kX2dlbmVzLCBtdWx0aV9iYW5kX2dlbmVzKQoKIyBOb3cgY3JlYXRlIGEgZGF0YS5mcmFtZSB3aXRoIHRoZSBnZW5lLWxldmVsIHN0YXR1cyBjYWxscyBmb3IgdGhlCiMgbmV1dHJhbCwgdW5jYWxsYWJsZSwgYW5kIHVuc3RhYmxlIGN5dG9iYW5kLWxldmVsIGFuZCBjaHJvbW9zb21lIGFybS1sZXZlbAojIGNhbGxzCmZpbHRlcmVkX2NvbnNlbnN1c19nZW5lX3N0YXR1cyA8LSBnZW5lX2RmICU+JQogIGxlZnRfam9pbigKICAgIGNvbnNlbnN1c19zZWdfYXJtX3N0YXR1cywKICAgIGJ5ID0gYygiYmlvc3BlY2ltZW5faWQiID0gIktpZHNfRmlyc3RfQmlvc3BlY2ltZW5fSUQiLAogICAgICAgICAgICJjaHJvbW9zb21lX2FybSIpCiAgKSAlPiUKICBsZWZ0X2pvaW4oCiAgICBjb25zZW5zdXNfc2VnX2N5dG9iYW5kX3N0YXR1cywKICAgIGJ5ID0gYygiYmlvc3BlY2ltZW5faWQiID0gIktpZHNfRmlyc3RfQmlvc3BlY2ltZW5fSUQiLAogICAgICAgICAgICJjeXRvYmFuZCIpCiAgKSAlPiUKICAjIEZpbHRlciB0aGUgYW5ub3RhdGVkIENOIGRhdGEgdG8gaW5jbHVkZSBvbmx5IG5ldXRyYWwgYXJtcyBhbmQgY3l0b2JhbmRzLAogICMgYW5kIGRpc2FncmVlbWVudHMKICBmaWx0ZXIoCiAgICAjIENhc2UgMSkgR2VuZSBjYWxsIGRpc2FncmVlcyB3aXRoIGJvdGggYXJtIGFuZCBjeXRvYmFuZCAoVGhpcyBjYXB0dXJlcyBtb3N0CiAgICAjIHdlIHdhbnQgdG8ga2VlcCwgaW5jbHVkaW5nIGFsbCBvZiB3aGVuIGFybSBhbmQgY3l0b2JhbmQgYXJlIG5ldXRyYWwsIHVuY2FsbGFibGUKICAgICMgb3IgdW5zdGFibGUsIHNpbmNlIHdlIGhhdmUgbm8gbmV1dHJhbCBnZW5lIGNhbGxzIGluIHRoaXMgZGYpOgogICAgKAogICAgICBzdGF0dXMgIT0gZG9taW5hbnRfYXJtX3N0YXR1cyAmIHN0YXR1cyAhPSBkb21pbmFudF9jeXRvYmFuZF9zdGF0dXMKICAgICkKICAgICMgQ2FzZSAyKSBHZW5lIGNhbGwgZGlzYWdyZWVzIHdpdGggYSBub24tbmV1dHJhbCBjeXRvYmFuZCBjYWxsLgogICAgIyBLZWVwIG5vIG1hdHRlciB3aGF0IHRoZSBhcm0gc3RhdHVzCiAgICB8ICgKICAgICAgc3RhdHVzICE9IGRvbWluYW50X2N5dG9iYW5kX3N0YXR1cwogICAgICAmIGRvbWluYW50X2N5dG9iYW5kX3N0YXR1cyAlaW4lIGMoImdhaW4iLCAibG9zcyIpCiAgICApCiAgICAjIEkgdGhpbmsgdGhhdCBjYXB0dXJlcyBldmVyeXRoaW5nIHdlIHdhbnQuIENhc2VzIHdlIGRvbid0IHdhbnQgaW5jbHVkZToKICAgICMgZ2VuZSAmIGFybSBhZ3JlZSwgYnV0IGN5dG9iYW5kIGlzIG5ldXRyYWwKICAgICMgZ2VuZSAmIGN5dG9iYW5kIGFncmVlCiAgICAjIGFsbCAzIGFncmVlCiAgKSAlPiUKICBzZWxlY3QoS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRCA9IGJpb3NwZWNpbWVuX2lkLAogICAgICAgICBnZW5lX3N5bWJvbCwKICAgICAgICAgc3RhdHVzKSAlPiUKICAjIFRoZSBgZGlzdGluY3QoKWAgZnVuY3Rpb24gaXMgbmVlZGVkIHRvIHJlbW92ZSBkdXBsaWNhdGVzIHJlc3VsdGluZyBmcm9tCiAgIyB0aGUgYmFuZCBzZXBhcmF0aW9uIGludG8gbXVsdGlwbGUgcm93cyBzdGVwIGFib3ZlCiAgZGlzdGluY3QoKQoKIyBEaXNwbGF5IHRhYmxlCmZpbHRlcmVkX2NvbnNlbnN1c19nZW5lX3N0YXR1cwpgYGAKCiMjIENvbWJpbmUgYXJtLCBjeXRvYmFuZCwgYW5kIGdlbmUtbGV2ZWwgc3RhdHVzIGRhdGEKCmBgYHtyfQojIFJlbmFtZSBlYWNoIHRoZSBkb21pbmFudCBzdGF0dXMgY29sdW1ucyBvZiB0aGUgZGF0YS5mcmFtZXMKIyB0byBiZSB1bmlmb3JtIGZvciB0aGUgYmluZGluZyByb3dzIHN0ZXAgYW5kIGZpbHRlciBvdXQgIm5ldXRyYWwiIGNhbGxzCmNvbnNlbnN1c19zZWdfYXJtX3N0YXR1cyA8LSBjb25zZW5zdXNfc2VnX2FybV9zdGF0dXMgJT4lCiAgZmlsdGVyKCEoZG9taW5hbnRfYXJtX3N0YXR1cyA9PSAibmV1dHJhbCIpKSAlPiUKICByZW5hbWUoZG9taW5hbnRfc3RhdHVzID0gZG9taW5hbnRfYXJtX3N0YXR1cykKCmZpbHRlcmVkX2NvbnNlbnN1c19jeXRvYmFuZF9zdGF0dXMgPC0gZmlsdGVyZWRfY29uc2Vuc3VzX2N5dG9iYW5kX3N0YXR1cyAlPiUKICBmaWx0ZXIoIShkb21pbmFudF9jeXRvYmFuZF9zdGF0dXMgPT0gIm5ldXRyYWwiKSkgJT4lCiAgcmVuYW1lKGRvbWluYW50X3N0YXR1cyA9IGRvbWluYW50X2N5dG9iYW5kX3N0YXR1cykKCiMgVGhlcmUgYXJlIG5vICJuZXV0cmFsIiBjYWxscyBhdCB0aGUgZ2VuZSBsZXZlbCBzbyB3ZSBkbyBub3QgbmVlZCB0byBmaWx0ZXIKIyBvdXQgdGhvc2UgY2FsbHMgaGVyZQpmaWx0ZXJlZF9jb25zZW5zdXNfZ2VuZV9zdGF0dXMgPC0gZmlsdGVyZWRfY29uc2Vuc3VzX2dlbmVfc3RhdHVzICU+JQogIHJlbmFtZShkb21pbmFudF9zdGF0dXMgPSBzdGF0dXMpCgojIEZvciBlYWNoIG9mIHRoZSBkYXRhc2V0cyB3ZSdyZSBqb2luaW5nLCB3ZSdsbCBvbmx5IGtlZXAgdGhlc2UgY29sdW1uczoKY29sc190b19rZWVwIDwtIGMoIktpZHNfRmlyc3RfQmlvc3BlY2ltZW5fSUQiLCAiZG9taW5hbnRfc3RhdHVzIikKYGBgCgpDb21iaW5lIGludG8gb25lIGxvbmcgZGF0YSBmcmFtZQoKYGBge3J9CiMgQ29tYmluZSB0aGUgYXJtLCBjeXRvYmFuZCwgYW5kIGdlbmUgc3RhdHVzIGNvdW50IGRhdGEKZmluYWxfZGYgPC0gYmluZF9yb3dzKAogIGFybSA9IHNlbGVjdChjb25zZW5zdXNfc2VnX2FybV9zdGF0dXMsCiAgICAgICAgICAgICAgIGNvbHNfdG9fa2VlcCwKICAgICAgICAgICAgICAgcmVnaW9uID0gY2hyb21vc29tZV9hcm0pLAogIGN5dG9iYW5kID0gc2VsZWN0KGZpbHRlcmVkX2NvbnNlbnN1c19jeXRvYmFuZF9zdGF0dXMsCiAgICAgICAgICAgICAgICAgICAgY29sc190b19rZWVwLAogICAgICAgICAgICAgICAgICAgIHJlZ2lvbiA9IGN5dG9iYW5kKSwKICBnZW5lID0gc2VsZWN0KGZpbHRlcmVkX2NvbnNlbnN1c19nZW5lX3N0YXR1cywKICAgICAgICAgICAgICAgIGNvbHNfdG9fa2VlcCwKICAgICAgICAgICAgICAgIHJlZ2lvbiA9IGdlbmVfc3ltYm9sKSwKICAuaWQgPSAicmVnaW9uX3R5cGUiCikgJT4lCiAgIyBSZW9yZGVyIGNvbHVtbnMgbW9yZSBzZW5zaWJseQogIHNlbGVjdChLaWRzX0ZpcnN0X0Jpb3NwZWNpbWVuX0lELAogICAgICAgICBzdGF0dXMgPSBkb21pbmFudF9zdGF0dXMsCiAgICAgICAgIHJlZ2lvbiwKICAgICAgICAgcmVnaW9uX3R5cGUpICU+JQogIGFycmFuZ2UoS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRCkKCiMgUHJpbnQgb3V0IHByZXZpZXcKZmluYWxfZGYKYGBgCgojIyBXcml0ZSB0byBhIFRTViBmaWxlIAoKYGBge3J9CiMgV3JpdGUgZmluYWwgbG9uZyBzdGF0dXMgdGFibGUgdG8gZmlsZQp3cml0ZV90c3YoCiAgZmluYWxfZGYsCiAgZmlsZS5wYXRoKHJlc3VsdHNfZGlyLCAiY29uc2Vuc3VzX3NlZ19tb3N0X2ZvY2FsX2NuX3N0YXR1cy50c3YuZ3oiKQopCgojIERpc3BsYXkgZmluYWwgbG9uZyBzdGF0dXMgdGFibGUKZmluYWxfZGYgJT4lCiAgYXJyYW5nZShyZWdpb25fdHlwZSkKYGBgCgojIyBTZXNzaW9uIEluZm8KCmBgYHtyfQpzZXNzaW9uSW5mbygpCmBgYAo=